PostgreSQL Keepalived 配置
1 Keepalived 服务配置
1.1 node1 配置
1、编辑keepalived.conf
su - root
vi /etc/keepalived/keepalived.conf
2、配置如下
! Configuration File for keepalived
global_defs {
# 路由器标识,一般不用改,也可以写成每个主机自己的主机名
router_id LVS_DEVEL
# 严格执行VRRP 协议。
vrrp_skip_check_adv_addr
# 使用 FIFO 先进先出算法的优先级
vrrp_strict
# Master发送 NA 信息的延迟
vrrp_garp_interval 0
# Master发送 ARP/NA 消息延迟
vrrp_gna_interval 0
}
# 定义用于实例执行的脚本内容,比如可以在线降低优先级,用于强制切换
vrrp_script check_pg_alived {
script "/soft/scripts/pg_monitor.sh"
interval 10
fall 3
}
# 一个vrrp_instance就是定义一个虚拟路由器的,实例名称
vrrp_instance VI_1 {
# 定义初始状态,可以是MASTER或者BACKUP
state MASTER
#非抢占模式
nopreempt
# 工作接口,通告选举使用哪个接口进行
interface ens192
# 虚拟路由ID,如果是一组虚拟路由就定义一个ID,如果是多组就要定义多个,而且这个虚拟
# ID还是虚拟MAC最后一段地址的信息,取值范围0-255
virtual_router_id 51
#权重 如果你上面定义了MASTER,这里的优先级就需要定义的比其他的高
priority 100
#通告频率 单位s
advert_int 1
# 通信认证机制,这里是明文认证还有一种是加密认证
authentication {
auth_type PASS
auth_pass 1111
}
# 设置虚拟VIP地址
virtual_ipaddress {
192.168.10.172
}
# 追踪脚本,通常用于去执行上面的vrrp_script定义的脚本内容
track_script {
check_pg_alived
}
# 如果主机状态变成`Master|Backup|Fault`之后会去执行的通知脚本,脚本要自己写
notify_master "/soft/scripts/failover.sh"
notify_fault "/soft/scripts/fault.sh"
}
以上是Keepalived主节点的配置。
1.2 node2 配置
1、priority参数改成90 。
2、state改为BACKUP
3、其余参数配置一样。
! Configuration File for keepalived
global_defs {
# 路由器标识,一般不用改,也可以写成每个主机自己的主机名
router_id LVS_DEVEL
# 严格执行VRRP 协议。
vrrp_skip_check_adv_addr
# 使用 FIFO 先进先出算法的优先级
vrrp_strict
# Master发送 NA 信息的延迟
vrrp_garp_interval 0
# Master发送 ARP/NA 消息延迟
vrrp_gna_interval 0
}
# 定义用于实例执行的脚本内容,比如可以在线降低优先级,用于强制切换
vrrp_script check_pg_alived {
script "/soft/scripts/pg_monitor.sh"
interval 10
fall 3
}
# 一个vrrp_instance就是定义一个虚拟路由器的,实例名称
vrrp_instance VI_1 {
# 定义初始状态,可以是MASTER或者BACKUP
state BACKUP
#非抢占模式
nopreempt
# 工作接口,通告选举使用哪个接口进行
interface ens192
# 虚拟路由ID,如果是一组虚拟路由就定义一个ID,如果是多组就要定义多个,而且这个虚拟
# ID还是虚拟MAC最后一段地址的信息,取值范围0-255
virtual_router_id 51
#权重 如果你上面定义了MASTER,这里的优先级就需要定义的比其他的高
priority 90
#通告频率 单位s
advert_int 1
# 通信认证机制,这里是明文认证还有一种是加密认证
authentication {
auth_type PASS
auth_pass 1111
}
# 设置虚拟VIP地址
virtual_ipaddress {
192.168.10.172
}
# 追踪脚本,通常用于去执行上面的vrrp_script定义的脚本内容
track_script {
check_pg_alived
}
# 如果主机状态变成`Master|Backup|Fault`之后会去执行的通知脚本,脚本要自己写
notify_master "/soft/scripts/failover.sh"
notify_fault "/soft/scripts/fault.sh"
}
2 keepalived 脚本配置
以下操作在node1 上执行,使用scp 方式拷贝到所有节点上。
2.1 数据库监控脚本(pg_monitor.sh)
此脚本每隔10秒执行一次,执行频率由 keepalived.conf
配置文件中 interval
参数设置,脚本主要作用为:
1、检测主库是否存活。
2、更新sr_delay表last_alive字段为当前探测时间。
3、具体内容如下:
su - root
vi /soft/scripts/pg_monitor.sh
--------------------input------------------------------
#!/bin/bash
# 配置环境变量
PGPORT=5432
PGUSER=keepalived
PGDB=keepalived
PGPWD='keepalived'
Lang=en_US.utf8
PGHOME=/usr/local/pgsql/
LD_LIBRARY_PATH=$PGHOME/lib:/lib64:/usr/lib64:/usr/local/lib64:/lib:/usr/lib:/usr/local/lib
PATH=$PGHOME/bin:$PATH:.
MONITOR_LOG="/soft/scripts/pg_monitor.log"
SQL1="UPDATE sr_delay SET last_alive= now();"
SQL2='SELECT 1;'
keeplognums=30000
#此脚本不检查备库存活状态,如果是备库库则退出
standby_flg=`psql -p$PGPORT -U$PGUSER -At -c "SELECT pg_is_in_recovery();"`
if [ ${standby_flg} == 't' ];
then
echo ‐e "`date +%F\ %T`: This is a standby database, exit!\n" > $MONITOR_LOG
exit 0
fi
export PGPASSWORD=$PGPWD
#主库更新sr_delay 表
echo $SQL1 | psql -At -p $PGPORT -U $PGUSER -d $PGDB >> $MONITOR_LOG
#判断主库是否可用
echo $SQL2 | psql -At -p $PGPORT -U $PGUSER -d $PGDB
if [ $? -eq 0 ] ;
then
echo -e "`date +%F\ %T`:Primary db is health." >> $MONITOR_LOG
exit 0
else
echo -e "`date +%F\ %T`:Attention: Primary db is not health!" >> $MONITOR_LOG
exit 1
fi
#日志保留 keeplognums 行
if [ ! -f ${MONITOR_LOG} ] ;then touch ${MONITOR_LOG};fi
lognums=`cat ${MONITOR_LOG} |wc -l`
catnum=$((${lognums} -${keeplognums}))
if [[ $lognums -gt ${keeplognums} ]] ; then sed -i "1,${catnum}d" ${MONITOR_LOG}; fi
2.2 数据库主备切换脚本(failover.sh)
此脚本当主库没有存活,进行主备切换。脚本主要作用为:
1、检测主库是否存活。
2、当 sr_allowed_delay_time
超过100秒后。
3、执行主备切换。
vi /soft/scripts/failover.sh
--------------------input------------------------------
#!/bin/bash
export PGPORT=5432
export PGUSER=keepalived
export PG_OS_USER=postgres
export PGDBNAME=keepalived
export LANG=en_US.UTF-8
export PGPATH=/usr/local/pgsql/bin/
export PATH=$PATH:$PGPATH
export PGMIP=127.0.0.1
LOGFILE='/soft/scripts/failover.log'
# 主备数据库同步时延,单位为秒
sr_allowed_delay_time=100
SQL1='SELECT pg_is_in_recovery FROM pg_is_in_recovery();'
SQL2="SELECT count(1) as delay_time FROM sr_delay WHERE now()<(last_alive + interval '${sr_allowed_delay_time} seconds');"
#SQL2="select count(1) as delay_time from sr_delay where now()<(last_alive + interval '100 seconds');"
sleep $sr_allowed_delay_time
db_role=`echo $SQL1 | psql -At -p $PGPORT -U $PGUSER -d $PGDBNAME -w`
db_sr_delaytime=`echo $SQL2 | psql -p $PGPORT -d $PGDBNAME -U $PGUSER -At -w`
SWITCH_COMMAND='pg_ctl promote -D /usr/local/pgsql/data'
# 如果为备库,且延迟大于指定时间则切换为主库
if [ ${db_role} == 'f' ];
then
echo -e `date +"%F %T"` "Attention:The current postgreSQL DB is master database,cannot switched!" >> $LOGFILE
exit 0
fi
if [ $db_sr_delaytime -gt 0 ];
then
echo -e `date +"%F %T"` "Attention:The current master database is health,the standby DB cannot switched!" >> $LOGFILE
exit 0
fi
if [ !$db_sr_delaytime ];
then
echo -e `date +"%F %T"` "Attention:The current database is statndby,ready to switch master database!" >> $LOGFILE
su - $PG_OS_USER -c "$SWITCH_COMMAND"
elif [ $? -eq 0 ];
then
echo -e `date +"%F %T"` "success:The current standby database successed to switched the primary PG database !" >> $LOGFILE
exit 0
else
echo -e `date +"%F %T"` "Error: the standby database failed to switch the primary PG database !,pelease checked it!" >> $LOGFILE
exit 1
fi
2.3 数据库失败时,关闭 keepalived 脚本(fault.sh)
此脚本主要作用为:
1、检测数据库是否存活。
2、当数据库宕机时,将会关闭本节点的数据库和keepalived 服务。
vi /soft/scripts/fault.sh
--------------------input------------------------------
#!/bin/bash
GFILE=/soft/scripts/pg_db_fault.log
PGPORT=5432
PGMIP=127.0.0.1
echo -e `date +"%F %T"` "Error:Because of the priamry DB happend some unknown problem,So turn off the PostgreSQL Database!" >> $LOGFILE
#PGPID="`head -n1 /usr/local/pgsql/data/postmaster.pid`"
systemctl stop keepalived
#kill -9 $PGPID
if [ $? -eq 0 ] ;
then
echo -e `date +"%F %T"` "Error:Because of the priamry DB happend some unknown problem,So turn off the PostgreSQL Database!" >> $LOGFILE
systemctl stop keepalived
exit 1
fi
2.4 脚本授权并拷贝到其他节点
chmod +x /soft/scripts/pg_monitor.sh
chmod +x /soft/scripts/failover.sh
chmod +x /soft/scripts/fault.sh
ll /soft/scripts/*
scp /soft/scripts/* node2:/soft/scripts
2.5 启动keepalived 服务
所有节点执行。
su - root
systemctl start keepalived.service ;
systemctl enable keepalived.service ;
systemctl status keepalived.service ;
到此为止:打快照为 keepalived install
。
3 切换演练 (node1为主)
3.1 node1 数据库停止
su - postgres
pg_ctl -D $PGDATA stop
3.2 node1 的Keepalive 自动关闭
systemctl status keepalived.service ;
3.3 node2 成为 Master
psql -U postgres -d testdb
SELECT pg_is_in_recovery();
ALTER SYSTEM SET synchronous_commit ='local';
SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
-------------------
INSERT INTO t01 VALUES (1);
INSERT 0 1
3.4 node1 数据库修复
使用pg_rewind 同步新主库的数据到原主库。
pg_ctl start
pg_ctl stop
pg_rewind -D $PGDATA --source-server 'host=node2 port=5432 user=postgres password=postgres dbname=testdb' -P
如果这个地方咱们的wal日志已经被覆盖了了很多,那么就需要将我们的备份日志/archive/给scp过来。
scp postgres@192.168.254.129 :/data/pg_archive/00000002* /data/pg_archive/
(此时需要注意新主和备上面TimeLineID的差异。并且这种情况需要在配置文件中加上:
restore_command='cp /data/pg_archive/%f %p'
3.5 node1 重新加入到集群中
1、node1 修改postgresql.conf
vi $PGDATA/postgresql.conf
//屏幕输出:
primary_conninfo = 'user=repl password=repl host=node2 port=5432 application_name=node1'
recovery_target_timeline='latest'
primary_slot_name ='slot_node1'
3、node1 配置 standby.signal 文件
touch $PGDATA/standby.signal
2、node2 创建复制槽
psql -h node2 -U postgres -d testdb
SELECT * FROM pg_create_physical_replication_slot('slot_node1');
SELECT * FROM pg_replication_slots;
4、node1 检查主备wal sender和receiver是否正常:
pg_ctl -D $PGDATA start
ps axu |grep walreceiver
5、node1 启动Keepalived,查看Keepalived状态。
systemctl start keepalived
systemctl status keepalived
4 切换演练(node2为主)
在做切换前,先要检查 node1 和 node2 上面的Keepalived正常以及vip和主从正常,在 node2 上停掉数据库:
4.1 node2 数据库停止
pg_ctl -D $PGDATA stop
4.2 node1 的Keepalive 自动关闭
systemctl status keepalived.service ;
4.3 node1 成为 Master
psql -U postgres -d testdb
SELECT pg_is_in_recovery();
ALTER SYSTEM SET synchronous_commit ='local';
SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
-------------------
INSERT INTO t01 VALUES (1);
INSERT 0 1
4.4 node2 数据库修复
使用pg_rewind 同步新主库的数据到原主库:
pg_ctl start
pg_ctl stop
pg_rewind -P -D $PGDATA --source-server 'host=node1 port=5432 user=postgres password=postgres dbname=testdb'
如果这个地方咱们的wal日志已经被覆盖了了很多,那么就需要将我们的备份日志/archive/给scp过来。
scp postgres@192.168.254.129 :/data/pg_archive/00000002* /data/pg_archive/
(此时需要注意新主和备上面TimeLineID的差异。并且这种情况需要在配置文件中加上:
restore_command='cp /data/pg_archive/%f %p'
4.5 node2 重新加入到集群中
1、node2 修改postgresql.conf
vi $PGDATA/postgresql.conf
//屏幕输出:
primary_conninfo = 'user=postgres password=postgres host=node1 port=5432 application_name=node2'
recovery_target_timeline='latest'
primary_slot_name ='slot_node2'
3、node2 配置 standby.signal 文件
touch $PGDATA/standby.signal
2、node1 创建复制槽
psql -h node1 -U postgres -d testdb
SELECT * FROM pg_create_physical_replication_slot('slot_node2');
SELECT * FROM pg_replication_slots;
4、node1 检查主备wal sender和receiver是否正常:
pg_ctl -D $PGDATA start
ps axu |grep walreceiver
//屏幕输出:
postgres 3076 0.0 0.1 287660 9812 ? Ss 10:33 0:00 postgres: walreceiver streaming 0/A029E50
5、node1 启动Keepalived,查看Keepalived状态。
systemctl start keepalived
systemctl status keepalived
5 小结
1、可以完善failover时自己的延迟切换逻辑。
2、可以完善pg_rewind的实现,用脚本代替手动方式。
3、Keepalived较为灵活,能够在脚本上添加更多校验和自己的规则。